
#include <asset/assetsystem.h>
#include <asset/reader/assetreaderscenenode.h>
#include <fs/fileinfo.h>
#include <gfx/material.h>
#include <gfx/mesh.h>
#include <scene/entitynode.h>
#include <scene/geometry.h>
#include <scene/node.h>
#include <vfs.h>
#include <cffformat.h>


ceAssetReaderSceneNode::ceAssetReaderSceneNode()
	: iAssetReader ()
{
	CE_OBJECT_CONSTR
}


bool ceAssetReaderSceneNode::CanRead(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	if (type != ceNode::AssetTypeID)
		{
			return false;
		}

	ceFileInfo info (locator.GetLocation());
	std::string ext = info.GetExtension();
	return ext == "node";
}

ceRef<ceAsset> ceAssetReaderSceneNode::Read(ceEngine *engine, ceAssetTypeID type, const ceAssetLocator &locator)
{
	ceReleasePtr<iFile> file (ceVFS::Get()->Open(locator.GetLocation()));
	if (!file)
		{
			return 0;
		}
	long size = file->GetLength();

	char* buffer = new char[size+1];
	file->Read(buffer, size);
	file->Close ();

	buffer[size] = '\0';

	cffParser parser;
	ceReleasePtr<cffNode> parent (parser.ParseFromBuffer(buffer));
	delete [] buffer;
	if (!parent)
		{
			return 0;
		}



  unsigned numNodes = parent->GetNumberOfNodes("node");
  if (numNodes > 1)
    {
      // there are more than one node within the file...
      // so we create a base node for grouping them together.

      ceNode* parentNode = new ceNode ();
      parentNode->SetLocator(locator);

      for (unsigned i=0, ni=parent->GetNumberOfNodes(); i<ni; ++i)
        {
          cffNode* nodeNode = parent->GetNode(i);
          ceNode *n = ReadNode(engine, nodeNode);
          if (n)
            {
              parentNode->AddNode(n);
              n->Release();
            }
        }

      return parentNode;
    }
  else if (numNodes == 1)
    {
      ceNode* node = ReadNode(engine, parent->GetNode("node", 0));
      node->SetLocator(locator);
      return node;
    }


  return 0;
}

ceNode* ceAssetReaderSceneNode::ReadNode(ceEngine *engine, cffNode *node)
{
  if (!node
      || std::string ("node") != std::string(node->GetName()))
    {
      return 0;
    }



  std::string nodeType = "node";
  if (node->HasNamedAttribute("type"))
    {
      nodeType = node->GetNamedAttribute("type");
    }

  ceNode* resultNode = 0;
  if (nodeType == std::string ("node"))
    {
      resultNode = new ceNode ();
    }
  else if (nodeType == std::string ("entity"))
    {
      ceEntityNode* entity = new ceEntityNode ();

      cffNode* geometryNode = node->GetNode("geometry");
      if (geometryNode)
        {
          ceGeometry* geometry = new ceGeometry ();

          for (unsigned i=0, ni=geometryNode->GetNumberOfNodes(); i<ni; ++i)
            {
              cffNode* aNode = geometryNode->GetNode(i);
              if (!aNode)
                {
                  continue;
                }
              std::string aNodeName = aNode->GetName();
              if (aNodeName == std::string ("staticmesh"))
                {
                  cffNode* filenameNode = aNode->GetNode("filename");
                  if (filenameNode && filenameNode->HasAttribute(0))
                    {
                      ceMesh* mesh = engine->GetAssetSystem()->GetAsset<ceMesh>(ceAssetLocator(filenameNode->GetAttribute(0)));
                      iStaticMesh* staticMesh = engine->GetDevice()->CreateStaticMesh();
                      staticMesh->SetMesh(mesh);
                      geometry->SetMesh(staticMesh);
                      staticMesh->Release();
                    }

                }
              else if (aNodeName == std::string ("material"))
                {
                  if (aNode->HasAttribute(0))
                    {
                      ceMaterial* material = engine->GetAssetSystem()->GetAsset<ceMaterial>(ceAssetLocator(aNode->GetAttribute(0)));
                      geometry->SetMaterial(material);
                    }

                }
            }
          entity->SetGeometry(geometry);
        }


      resultNode = entity;
    }

  if (!resultNode)
    {
      return 0;
    }

  cffNode* transformationNode = node->GetNode("transformation");
  if (transformationNode)
    {
      cffNode* matrixNode = transformationNode->GetNode("matrix");
      if (matrixNode)
        {
          ceMatrix4f mat;
#define SET_MAT(v, a) if (matrixNode->HasAttribute(a)) mat.v = matrixNode->GetAttribute(a).AsFloat()
          SET_MAT(_00,  0);
          SET_MAT(_01,  1);
          SET_MAT(_02,  2);
          SET_MAT(_03,  3);
          SET_MAT(_10,  4);
          SET_MAT(_11,  5);
          SET_MAT(_12,  6);
          SET_MAT(_13,  7);
          SET_MAT(_20,  8);
          SET_MAT(_21,  9);
          SET_MAT(_22, 10);
          SET_MAT(_23, 11);
          SET_MAT(_30, 12);
          SET_MAT(_31, 13);
          SET_MAT(_32, 14);
          SET_MAT(_33, 15);
          resultNode->SetMatrix(mat);
        }
    }

  // now check for more child nodes
  unsigned numNodes = node->GetNumberOfNodes("node");
  if (numNodes > 0)
    {
      for (unsigned i=0, ni=node->GetNumberOfNodes(); i<ni; ++i)
        {
          cffNode* nodeNode = node->GetNode(i);
          ceNode *n = ReadNode(engine, nodeNode);
          if (n)
            {
              resultNode->AddNode(n);
              n->Release();
            }
        }

    }


  return resultNode;
}


